﻿#include "globaldata.h"
#include <QXmlStreamReader>
#include <QFileInfo>
#include <QDir>
#include <QDebug>

static void parseUnitAttributes(QXmlStreamReader& xml, Unit& unit)
{
    QXmlStreamAttributes attrs = xml.attributes();
    unit.id = attrs.value("id").toString();
    unit.type = attrs.value("type").toString();
    unit.name = attrs.value("name").toString();
    unit.desc = attrs.value("desc").toString();
}

static void parseUnitPos(QXmlStreamReader& xml, Unit& unit, const QString& basePath)
{
    QXmlStreamAttributes attrs = xml.attributes();
    unit.pos.x = attrs.value("x").toString();
    unit.pos.y = attrs.value("y").toString();
    unit.pos.w = attrs.value("w").toString();
    unit.pos.h = attrs.value("h").toString();
    unit.pos.angle = attrs.value("angle").toString();

    QString relativePath = attrs.value("icon").toString();
    unit.iconPath = QDir::cleanPath(basePath + QDir::separator() + relativePath);
}

static Pin parsePinAttributes(QXmlStreamReader& xml)
{
    QXmlStreamAttributes attrs = xml.attributes();
    Pin pin;
    pin.index = attrs.value("index").toInt();
    pin.name = attrs.value("name").toString();
    pin.unit = attrs.value("unit").toString();
    pin.desc = attrs.value("desc").toString();
    pin.dir = attrs.value("dir").toString();
    pin.value = attrs.value("value").toString();
    return pin;
}

static void parseCodeAttributes(QXmlStreamReader& xml, Unit& unit)
{
    QXmlStreamAttributes attrs = xml.attributes();
    unit.codeType = attrs.value("type").toString();
    unit.codeValue = attrs.value("value").toString();
}

static void parseUnitsAttributes(QXmlStreamReader& xml, Units& units)
{
    QXmlStreamAttributes attrs = xml.attributes();
    units.name = attrs.value("name").toString();
}

static void parseConstautsAttributes(QXmlStreamReader& xml, Unit& unit)
{
    while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == "constants"))
    {
        xml.readNext();
        if(xml.tokenType() != QXmlStreamReader::StartElement || xml.name() != "constant")
            continue;
        QXmlStreamAttributes attrs = xml.attributes();
        Unit::Constaut constant;
        constant.index = attrs.value("index").toInt();
        constant.name = attrs.value("name").toString();
        constant.value = attrs.value("value").toString();
        constant.unit = attrs.value("unit").toString();
        constant.desc = attrs.value("desc").toString();
        unit.constants.append(constant);
    }
}

static void parsePinGroup(QXmlStreamReader& xml, Unit& unit, const QStringRef& groupType)
{
    while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == groupType))
    {
        xml.readNext();
        if(xml.tokenType() != QXmlStreamReader::StartElement || xml.name() != "pin")
            continue;

        Pin pin = parsePinAttributes(xml);
        pin.unitId = unit.id;

        if (groupType == "input")
        {
            pin.isInputPin = true;
            unit.inputPins.append(pin);
        }
        else if (groupType == "output")
        {
            pin.isInputPin = false;
            unit.outputPins.append(pin);
        }
    }
}

static Pin getUnitPin(QList<Unit>& units, QString unitId, int pinIndex)
{
    for (const Unit& unit : units)
    {
        if (unit.id == unitId)
        {
            if (pinIndex < unit.inputPins.size())
                return unit.inputPins.at(pinIndex);
            else if (pinIndex - unit.inputPins.size() < unit.outputPins.size())
                return unit.outputPins.at(pinIndex - unit.inputPins.size());
            else
                return Pin();
        }
    }
    return Pin();
}

static void parsePinMap(QXmlStreamReader &xml, const QStringRef& mapType, QList<std::pair<Pin, Pin>>& pinMap, QList<Unit>& units)
{
    pinMap.clear();
    while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == mapType))
    {
        xml.readNext();
        if (xml.tokenType() == QXmlStreamReader::StartElement &&
            xml.name() == "connect")
        {
            QXmlStreamAttributes attributes = xml.attributes();
            if (attributes.hasAttribute("from") && attributes.hasAttribute("frompin") &&
                attributes.hasAttribute("to") && attributes.hasAttribute("topin"))
            {
                QString fromUnitId = attributes.value("from").toString();
                int fromPin = attributes.value("frompin").toInt();
                QString toUnitId = attributes.value("to").toString();
                int toPin = attributes.value("topin").toInt();

                const Pin srcPin = getUnitPin(units, fromUnitId, fromPin);
                const Pin dstPin = getUnitPin(units, toUnitId, toPin);

                pinMap.push_back(std::make_pair(srcPin, dstPin));
            }
        }
    }
}


const Unit *GlobalData::findUnitById(QString id) const
{
    for (const auto& unit : m_units)
    {
        if (unit.id == id)
            return &unit;
    }
    return nullptr;
}

bool GlobalData::initCatalog(const QString &fileName)
{
    QFile file(fileName);
    if (!file.open(QFile::ReadOnly | QFile::Text))
        return false;

    qDebug() << "read Catalog succeed";
    QXmlStreamReader xml(&file);
    QString xmlFilePath = QFileInfo(file).absolutePath(); // 获取XML绝对路径

    Units currentunits;
    Unit currentUnit;

    while (!xml.atEnd() && !xml.hasError())
    {
        QXmlStreamReader::TokenType token = xml.readNext();
        if (token == QXmlStreamReader::StartElement)
        {
            const QStringRef elementName = xml.name();

            if(elementName == "units")
            {
                currentunits = Units();
                parseUnitsAttributes(xml, currentunits);
            }

            else if (elementName == "unit")
            {
                currentUnit = Unit();
                parseUnitAttributes(xml, currentUnit);
            }

            else if (elementName == "pos")
            {
                parseUnitPos(xml, currentUnit, xmlFilePath);
            }

            else if (elementName == "input" || elementName == "output")
            {
                parsePinGroup(xml, currentUnit, elementName);
            }

            else if(elementName == "code")
            {
                parseCodeAttributes(xml, currentUnit);
            }
            else if(elementName == "constants")
            {
                parseConstautsAttributes(xml, currentUnit);
            }
        }

        if (token == QXmlStreamReader::EndElement)
        {
            if(xml.name() == "unit")
                currentunits.units.append(currentUnit);
            if(xml.name() == "units")
                m_catalog.append(currentunits);
        }
    }
    return true;
}

bool GlobalData::initGlobalUnits(const QString &fileName)
{
    QFile file(fileName);
    if (!file.open(QFile::ReadOnly | QFile::Text))
        return false;

    qDebug() << "read GlobaData succeed";
    QXmlStreamReader xml(&file);
    QString xmlFilePath = QFileInfo(file).absolutePath(); // 获取XML绝对路径
    Unit currentUnit;

    while (!xml.atEnd() && !xml.hasError())
    {
        QXmlStreamReader::TokenType token = xml.readNext();
        if (token == QXmlStreamReader::StartElement)
        {
            const QStringRef elementName = xml.name();
            if (elementName == "unit")
            {
                currentUnit = Unit();
                parseUnitAttributes(xml, currentUnit);
            }

            else if (elementName == "pos")
            {
                parseUnitPos(xml, currentUnit, xmlFilePath);
            }

            else if (elementName == "input" || elementName == "output")
            {
                parsePinGroup(xml, currentUnit, elementName);
            }

            else if(elementName == "map")
            {
                parsePinMap(xml, elementName, m_pinMap, m_units); //引脚连线
            }
            else if(elementName == "code")
            {
                parseCodeAttributes(xml, currentUnit);
            }
            else if(elementName == "constants")
            {
                parseConstautsAttributes(xml, currentUnit);
            }
        }

        // 单元结束处理
        if (token == QXmlStreamReader::EndElement && xml.name() == "unit")
            m_units.append(currentUnit);
    }
    return true;
}

// Unit 类的 == 运算符重载
bool Unit::operator==(const Unit& lhs)
{
    return
        // 基础成员比较
        lhs.id == id &&
        lhs.type == type &&
        lhs.name == name &&
        lhs.desc == desc &&
        lhs.iconPath == iconPath &&

        // 位置信息比较
        lhs.pos.x == pos.x &&
        lhs.pos.y == pos.y &&
        lhs.pos.w == pos.w &&
        lhs.pos.h == pos.h &&
        lhs.pos.angle == pos.angle &&

        // 输入/输出引脚列表比较
        lhs.inputPins == inputPins &&
        lhs.outputPins == outputPins &&

        // 参数列表比较
        lhs.constants == constants &&

        // 代码类型和值比较
        lhs.codeType == codeType &&
        lhs.codeValue == codeValue;
}
